home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / tex / webtp55.arc / TANGLE.CHG < prev    next >
Text File  |  1989-12-05  |  63KB  |  1,879 lines

  1. % This is TANGLE.CHG for TURBO Pascal 5.5
  2. %
  3. % (c) 1989 by Peter Sawatzki <FE617@DHAFEU11.BITNET>
  4. %             Buchenhof 3, D-5800 Hagen 1 (Dahl), Germany (West)
  5. %
  6. %  Change History:
  7. %
  8. %  Initials:   PS = Peter Sawatzki, FE617@DHAFEU11
  9. %  =========   WGS = Wayne G. Sullivan, WSULIVAN@IRLEARN
  10. %              PB = Peter Breitenlohner, PEB@DM0MPI11
  11. %
  12. %  rel.  date      Author  description
  13. %  ====  ====      ======  ===========
  14. %  v0.1  2-Mar-88  PS      initial TP3 release
  15. %  v0.2  5-May-88  PS      array-like macros: "()" solution
  16. %  v0.3 22-Aug-88  PS      copy some Inlines from WGS
  17. %  v0.4  3-Sep-88  PS      better handling of shl and shr
  18. %  v0.5  8-Dec-88  PS      better array-like macros based on PB's solution
  19. %  v0.6 10-Dec-88  PS      TurboPascal-like hex constants
  20. %  v0.7  3-Jun-89  PS      include the mod/and, div/shr optimization
  21. %  v0.8 29-Jul-89  PS      @i option: (nested) include files
  22. %  v0.9  1-Aug-89  PS      multiple change files
  23. %  v1.0  3-Aug-89  PS      Inline assembler implemented
  24. %  v1.1  5-Dec-89  PS      kill error in multiple change file handling
  25. %
  26. %  Tangle/Compile Instructions:
  27. %  ============================
  28. %  TANGLE TANGLE /d /m /c
  29. %  TPC /$A+,O-,E-,N-,B-,I-,V-,S-,D- /$M$5000,0,$2000 TANGLE /M
  30. %
  31. %
  32. % kludge fillchar firstvar -> lastvar inserted
  33. %
  34. ────────────────────────────────────────────────────────────────
  35. @x l.22 m.0
  36. \def\PASCAL{Pascal}
  37. @y
  38. \def\PASCAL{Pascal}
  39. \def\TP{\hbox{Turbo Pascal 5.5}}
  40. @z
  41. ────────────────────────────────────────────────────────────────
  42. @x l.36 m.0
  43.   \vfill}
  44. @y
  45.   \centerline{(Changes for \TP, 5-Dec-89)}
  46.   \vfill}
  47. @z
  48. ────────────────────────────────────────────────────────────────
  49. @x l.64 m.1
  50. @d banner=='This is TANGLE, Version 4'
  51. @y
  52. @d banner=='This is TANGLE, Version 4/TP55 1.1'
  53. @z
  54. ────────────────────────────────────────────────────────────────
  55. @x l.75 m.2
  56. @d end_of_TANGLE = 9999 {go here to wrap it up}
  57. @y
  58. @z
  59. ────────────────────────────────────────────────────────────────
  60. @x l.77 m.2
  61. @p @t\4@>@<Compiler directives@>@/
  62. program TANGLE(@!web_file,@!change_file,@!Pascal_file,@!pool);
  63. label end_of_TANGLE; {go here to finish}
  64. const @<Constants in the outer block@>@/
  65. type @<Types in the outer block@>@/
  66. var @<Globals in the outer block@>@/
  67. @y
  68. @p program TANGLE;
  69. uses
  70.   Asm2Inl;
  71. const @<Constants in the outer block@>@/
  72. type @<Types in the outer block@>@/
  73. const @<Typed constants in the outer block@>@/
  74. var @/
  75.   firstvar: byte; @/
  76.   @<Globals in the outer block@>@/
  77.   lastvar: byte; @/
  78. @<Inline procedures and functions@>@/
  79. @<All purpose procedures and functions@>@/
  80. @z
  81. ────────────────────────────────────────────────────────────────
  82. @x l.94 m.3
  83. @d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
  84. @d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
  85. @y
  86. @d ifdef(#)==@={$ifdef @>#@=}@>
  87. @d endif==@={$endif}@>
  88. @d debug==ifdef(deb)
  89. @d gubed==endif
  90. @d Asm(#)==inline(@[#@])
  91. @z
  92. ────────────────────────────────────────────────────────────────
  93. @x l.99 m.3
  94. @d stat==@{ {change this to `$\\{stat}\equiv\null$'
  95.   when gathering usage statistics}
  96. @d tats==@t@>@} {change this to `$\\{tats}\equiv\null$'
  97.   when gathering usage statistics}
  98. @y
  99. @d stat==ifdef(sta)
  100. @d tats==endif
  101. @z
  102. ────────────────────────────────────────────────────────────────
  103. @x l.114 m.4
  104. @<Compiler directives@>=
  105. @{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
  106. @!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
  107. @y
  108. @<Inline proc...@>=
  109. function mavail: word;
  110. Asm(mov ah,$48/   {allocate memory}
  111.     mov bx,$FFFF/ {determine free memory}
  112.     int $21/
  113.     mov ax,bx);   {return size of largest available block}
  114.  
  115. function malloc (no: word): word;
  116. Asm(mov ah,$48/   {allocate memory}
  117.     pop bx/       {no of bytes}
  118.     int $21/
  119.     jnc ok/       {no error}
  120.     xor ax,ax/    {clear ax in case of error}
  121.  ok: );
  122.  
  123. procedure mfree (segm: word);
  124. Asm(mov ah,$49/ {free memory}
  125.     pop es/     {segment to free}
  126.     int $21);
  127. @z
  128. ────────────────────────────────────────────────────────────────
  129. @x l.139 m.6
  130. @d incr(#) == #:=#+1 {increase a variable by unity}
  131. @d decr(#) == #:=#-1 {decrease a variable by unity}
  132. @d loop == @+ while true do@+ {repeat over and over until a |goto| happens}
  133. @d do_nothing == {empty statement}
  134. @d return == goto exit {terminate a procedure call}
  135. @f return == nil
  136. @f loop == xclause
  137. @y
  138. @d incr(#) == Inc(#) {increase a variable by unity}
  139. @d decr(#) == Dec(#) {decrease a variable by unity}
  140. @d loop == @+ while true do@+ {repeat over and over until a |goto| happens}
  141. @d do_nothing == {empty statement}
  142. @d return == @= exit @>
  143. @f return == nil
  144. @f loop == xclause
  145. @d void == begin end
  146. @z
  147. ────────────────────────────────────────────────────────────────
  148. @x l.168 m.7
  149. @d othercases == others: {default for cases not listed explicitly}
  150. @y
  151. @d othercases == else {default for cases not listed explicitly}
  152. @z
  153. ────────────────────────────────────────────────────────────────
  154. @x l.177 m.8
  155. @!buf_size=100; {maximum length of input line}
  156. @!max_bytes=45000; {|1/ww| times the number of bytes in identifiers,
  157.   strings, and module names; must be less than 65536}
  158. @!max_toks=50000; {|1/zz| times the number of bytes in compressed \PASCAL\ code;
  159.   must be less than 65536}
  160. @!max_names=4000; {number of identifiers, strings, module names;
  161.   must be less than 10240}
  162. @y
  163. @!buf_size=256; {maximum length of input line (must be |>255| for inline code)}
  164. @!max_max_bytes=8000;
  165. @!min_bytes=1000;
  166. @!step_bytes=1000;
  167. @!max_bytes: word = max_max_bytes;
  168.  {|1/ww| times the number of bytes in identifiers,
  169.   strings, and module names; must be less than 65536}
  170. @!max_max_toks = 15000;
  171. @!min_toks = 2000;
  172. @!step_toks = 2000;
  173. @!max_toks: word = max_max_toks;
  174.  {|1/zz| times the number of bytes in compressed \PASCAL\ code;
  175.   must be less than 65536}
  176. @!max_names=4600; {number of identifiers, strings, module names;
  177.   must be less than 10240}
  178. @z
  179. ────────────────────────────────────────────────────────────────
  180. @x l.190 m.8
  181. @!max_id_length=12; {long identifiers are chopped to this length, which must
  182.   not exceed |line_length|}
  183. @!unambig_length=7; {identifiers must be unique if chopped to this length}
  184.   {note that 7 is more strict than \PASCAL's 8, but this can be varied}
  185. @y
  186. @!max_id_length=30; {long identifiers are chopped to this length, which must
  187.   not exceed |line_length|}
  188. @!unambig_length=25; {identifiers must be unique if chopped to this length}
  189. @z
  190. ────────────────────────────────────────────────────────────────
  191. @x l.301 m.12
  192. @!text_file=packed file of text_char;
  193. @y
  194. @!text_file=Text;
  195. @ @d term_out==Output
  196. @z
  197. ────────────────────────────────────────────────────────────────
  198. @x l.303 m.13
  199. @ The \.{WEAVE} and \.{TANGLE} processors convert between ASCII code and
  200. the user's external character set by means of arrays |xord| and |xchr|
  201. that are analogous to \PASCAL's |ord| and |chr| functions.
  202.  
  203. @<Globals...@>=
  204. @!xord: array [text_char] of ASCII_code;
  205.   {specifies conversion of input characters}
  206. @!xchr: array [ASCII_code] of text_char;
  207.   {specifies conversion of output characters}
  208.  
  209. @ If we assume that every system using \.{WEB} is able to read and write the
  210. visible characters of standard ASCII (although not necessarily using the
  211. ASCII codes to represent them), the following assignment statements initialize
  212. most of the |xchr| array properly, without needing any system-dependent
  213. changes. For example, the statement \.{xchr[@@\'101]:=\'A\'} that appears
  214. in the present \.{WEB} file might be encoded in, say, {\mc EBCDIC} code
  215. on the external medium on which it resides, but \.{TANGLE} will convert from
  216. this external code to ASCII and back again. Therefore the assignment
  217. statement \.{XCHR[65]:=\'A\'} will appear in the corresponding \PASCAL\ file,
  218. and \PASCAL\ will compile this statement so that |xchr[65]| receives the
  219. character \.A in the external (|char|) code. Note that it would be quite
  220. incorrect to say \.{xchr[@@\'101]:="A"}, because |"A"| is a constant of
  221. type |integer|, not |char|, and because we have $|"A"|=65$ regardless of
  222. the external character set.
  223.  
  224. @<Set init...@>=
  225. xchr[@'40]:=' ';
  226. xchr[@'41]:='!';
  227. xchr[@'42]:='"';
  228. xchr[@'43]:='#';
  229. xchr[@'44]:='$';
  230. xchr[@'45]:='%';
  231. xchr[@'46]:='&';
  232. xchr[@'47]:='''';@/
  233. xchr[@'50]:='(';
  234. xchr[@'51]:=')';
  235. xchr[@'52]:='*';
  236. xchr[@'53]:='+';
  237. xchr[@'54]:=',';
  238. xchr[@'55]:='-';
  239. xchr[@'56]:='.';
  240. xchr[@'57]:='/';@/
  241. xchr[@'60]:='0';
  242. xchr[@'61]:='1';
  243. xchr[@'62]:='2';
  244. xchr[@'63]:='3';
  245. xchr[@'64]:='4';
  246. xchr[@'65]:='5';
  247. xchr[@'66]:='6';
  248. xchr[@'67]:='7';@/
  249. xchr[@'70]:='8';
  250. xchr[@'71]:='9';
  251. xchr[@'72]:=':';
  252. xchr[@'73]:=';';
  253. xchr[@'74]:='<';
  254. xchr[@'75]:='=';
  255. xchr[@'76]:='>';
  256. xchr[@'77]:='?';@/
  257. xchr[@'100]:='@@';
  258. xchr[@'101]:='A';
  259. xchr[@'102]:='B';
  260. xchr[@'103]:='C';
  261. xchr[@'104]:='D';
  262. xchr[@'105]:='E';
  263. xchr[@'106]:='F';
  264. xchr[@'107]:='G';@/
  265. xchr[@'110]:='H';
  266. xchr[@'111]:='I';
  267. xchr[@'112]:='J';
  268. xchr[@'113]:='K';
  269. xchr[@'114]:='L';
  270. xchr[@'115]:='M';
  271. xchr[@'116]:='N';
  272. xchr[@'117]:='O';@/
  273. xchr[@'120]:='P';
  274. xchr[@'121]:='Q';
  275. xchr[@'122]:='R';
  276. xchr[@'123]:='S';
  277. xchr[@'124]:='T';
  278. xchr[@'125]:='U';
  279. xchr[@'126]:='V';
  280. xchr[@'127]:='W';@/
  281. xchr[@'130]:='X';
  282. xchr[@'131]:='Y';
  283. xchr[@'132]:='Z';
  284. xchr[@'133]:='[';
  285. xchr[@'134]:='\';
  286. xchr[@'135]:=']';
  287. xchr[@'136]:='^';
  288. xchr[@'137]:='_';@/
  289. xchr[@'140]:='`';
  290. xchr[@'141]:='a';
  291. xchr[@'142]:='b';
  292. xchr[@'143]:='c';
  293. xchr[@'144]:='d';
  294. xchr[@'145]:='e';
  295. xchr[@'146]:='f';
  296. xchr[@'147]:='g';@/
  297. xchr[@'150]:='h';
  298. xchr[@'151]:='i';
  299. xchr[@'152]:='j';
  300. xchr[@'153]:='k';
  301. xchr[@'154]:='l';
  302. xchr[@'155]:='m';
  303. xchr[@'156]:='n';
  304. xchr[@'157]:='o';@/
  305. xchr[@'160]:='p';
  306. xchr[@'161]:='q';
  307. xchr[@'162]:='r';
  308. xchr[@'163]:='s';
  309. xchr[@'164]:='t';
  310. xchr[@'165]:='u';
  311. xchr[@'166]:='v';
  312. xchr[@'167]:='w';@/
  313. xchr[@'170]:='x';
  314. xchr[@'171]:='y';
  315. xchr[@'172]:='z';
  316. xchr[@'173]:='{';
  317. xchr[@'174]:='|';
  318. xchr[@'175]:='}';
  319. xchr[@'176]:='~';@/
  320. xchr[0]:=' '; xchr[@'177]:=' '; {these ASCII codes are not used}
  321. @y
  322. @ The \.{WEAVE} and \.{TANGLE} processors convert between ASCII code and
  323. the user's external character set by means of arrays |xord| and |xchr|
  324. that are analogous to \PASCAL's |ord| and |chr| functions.
  325. The following typed constants define the |xchr| array properly.
  326.  
  327. @<Typed constants...@>=
  328. xchr: array [ASCII_code] of text_char=(@/
  329. ' ',' ',' ',' ',' ',' ',' ',' ',  ' ', #9,' ',' ',' ',#13,' ',' ',@/
  330. ' ',' ',' ',' ',' ',' ',' ',' ',  ' ',' ',' ',' ',' ',' ',' ',' ',@/
  331. ' ','!','"','#','$','%','&','''', '(',')','*','+',',','-','.','/',@/
  332. '0','1','2','3','4','5','6','7',  '8','9',':',';','<','=','>','?',@/
  333. '@@','A','B','C','D','E','F','G', 'H','I','J','K','L','M','N','O',@/
  334. 'P','Q','R','S','T','U','V','W',  'X','Y','Z','[','\',']','^','_',@/
  335. '`','a','b','c','d','e','f','g',  'h','i','j','k','l','m','n','o',@/
  336. 'p','q','r','s','t','u','v','w',  'x','y','z','{','|','}','~',' ',@/
  337. ' ',' ',' ',' ',' ',' ',' ',' ',  ' ',' ',' ',' ',' ',' ',' ',' ',@/
  338. ' ',' ',' ',' ',' ',' ',' ',' ',  ' ',' ',' ',' ',' ',' ',' ',' ',@/
  339. ' ',' ',' ',' ',' ',' ',' ',' ',  ' ',' ',' ',' ',' ',' ',' ',' ',@/
  340. ' ',' ',' ',' ',' ',' ',' ',' ',  ' ',' ',' ',' ',' ',' ',' ',' ',@/
  341. ' ',' ',' ',' ',' ',' ',' ',' ',  ' ',' ',' ',' ',' ',' ',' ',' ',@/
  342. ' ',' ',' ',' ',' ',' ',' ',' ',  ' ',' ',' ',' ',' ',' ',' ',' ',@/
  343. ' ',' ',' ',' ',' ',' ',' ',' ',  ' ',' ',' ',' ',' ',' ',' ',' ',@/
  344. ' ',' ',' ',' ',' ',' ',' ',' ',  ' ',' ',' ',' ',' ',' ',' ',' ');
  345.  
  346. @ The following definition makes the |xord| array contain a
  347. suitable inverse to the information in |xchr|.
  348.  
  349. @<Globals...@>=
  350. @!xord: array [text_char] of ASCII_code absolute xchr;
  351.   {specifies conversion of input characters}
  352. @z
  353. ────────────────────────────────────────────────────────────────
  354. @x l.443 m.16
  355. @ When we initialize the |xord| array and the remaining parts of |xchr|,
  356. it will be convenient to make use of an index variable, |i|.
  357.  
  358. @<Local variables for init...@>=
  359. @!i:0..255;
  360.  
  361. @ Here now is the system-dependent part of the character set.
  362. If \.{WEB} is being implemented on a garden-variety \PASCAL\ for which
  363. only standard ASCII codes will appear in the input and output files, you
  364. don't need to make any changes here. But if you have, for example, an extended
  365. character set like the one in Appendix~C of {\sl The \TeX book}, the first
  366. line of code in this module should be changed to
  367. $$\hbox{|for i:=1 to @'37 do xchr[i]:=chr(i);|}$$
  368. \.{WEB}'s character set is essentially identical to \TeX's, even with respect to
  369. characters less than @'40.
  370. @^system dependencies@>
  371.  
  372. Changes to the present module will make \.{WEB} more friendly on computers
  373. that have an extended character set, so that one can type things like
  374. \.^^Z\ instead of \.{<>}. If you have an extended set of characters that
  375. are easily incorporated into text files, you can assign codes arbitrarily
  376. here, giving an |xchr| equivalent to whatever characters the users of
  377. \.{WEB} are allowed to have in their input files, provided that unsuitable
  378. characters do not correspond to special codes like |carriage_return|
  379. that are listed above.
  380.  
  381. (The present file \.{TANGLE.WEB} does not contain any of the non-ASCII
  382. characters, because it is intended to be used with all implementations of
  383. \.{WEB}.  It was originally created on a Stanford system that has a
  384. convenient extended character set, then ``sanitized'' by applying another
  385. program that transliterated all of the non-standard characters into
  386. standard equivalents.)
  387.  
  388. @<Set init...@>=
  389. for i:=1 to @'37 do xchr[i]:=' ';
  390. for i:=@'200 to @'377 do xchr[i]:=' ';
  391.  
  392. @ The following system-independent code makes the |xord| array contain a
  393. suitable inverse to the information in |xchr|.
  394.  
  395. @<Set init...@>=
  396. for i:=first_text_char to last_text_char do xord[chr(i)]:=" ";
  397. for i:=1 to @'377 do xord[xchr[i]]:=i;
  398. xord[' ']:=" ";
  399. @y
  400. @ not neccesssary
  401.  
  402. @ not neccessary
  403.  
  404. @ not neccessary
  405. @z
  406. ────────────────────────────────────────────────────────────────
  407. @x l.511 m.20
  408. @!term_out:text_file; {the terminal as an output file}
  409. @y
  410. @z
  411. ────────────────────────────────────────────────────────────────
  412. @x l.519 m.21
  413. rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
  414. @y
  415. @z
  416. ────────────────────────────────────────────────────────────────
  417. @x l.526 m.22
  418. @d update_terminal == break(term_out) {empty the terminal output buffer}
  419. @y
  420. @d update_terminal ==
  421. @z
  422. ────────────────────────────────────────────────────────────────
  423. @x l.528 m.23
  424. @ The main input comes from |web_file|; this input may be overridden
  425. by changes in |change_file|. (If |change_file| is empty, there are no changes.)
  426.  
  427. @<Globals...@>=
  428. @!web_file:text_file; {primary input}
  429. @!change_file:text_file; {updates}
  430. @y
  431. @ We need some data structures to implement the include facility of
  432.   \.{TANGLE}
  433. @<Constants...@>=
  434. ChangeMax = 5; {maximal # of Changefiles}
  435. No_of_Files = 6; {Webfile + Changefiles + all Include files}
  436. buffer_size = 4*1024; {multiple of 16}
  437.  
  438. @ @<Globals...@>=
  439. FileMax: 0..No_of_Files; {how many files fit into memory}
  440. @!file_prev: array[1..No_of_Files] of word;
  441. @!file_ptr: array[1..No_of_Files] of word;
  442.  
  443. @ To access a textfile we use
  444. @d textf(#)==Text(Ptr(file_ptr[#],0)^)
  445.  
  446. @ The main input comes from |web_file|; this input may be overridden
  447. by changes in the |ChgFile|s. |ChgCnt| is the number of change files.
  448. (If |ChgCnt| is zero, there are no changes.)
  449.  
  450. @<Globals...@>=
  451. @!web_file:word; {primary input}
  452. @!ChgFile: array[1..ChangeMax] of word; {array of change files}
  453. @!ChgCnt: 0..ChangeMax; { # of change files in |ChgFile|}
  454. @!ChgAct: array[1..ChangeMax] of 1..ChangeMax; {active change files}
  455. @!ChgLevel: 0..ChangeMax; {# of active change files in |ChgAct|}
  456.  
  457. @z
  458. ────────────────────────────────────────────────────────────────
  459. @x l.535 m.24
  460. @ The following code opens the input files.  Since these files were listed
  461. in the program header, we assume that the \PASCAL\ runtime system has
  462. already checked that suitable file names have been given; therefore no
  463. additional error checking needs to be done.
  464. @^system dependencies@>
  465.  
  466. @p procedure open_input; {prepare to read |web_file| and |change_file|}
  467. begin reset(web_file); reset(change_file);
  468. end;
  469. @y
  470. @ The following code closes an input file. If the input file is the son
  471. of another input file, the higher level file is returned.
  472. @^system dependencies@>
  473.  
  474. @p procedure close_fil (var f: word);
  475.      var tf: word;
  476.      begin
  477.        if f<>0 then begin
  478.          Close(textf(f));
  479.          if IoResult<>0 then void;
  480.          tf:= f;
  481.          f:= file_prev[f];
  482.          file_prev[tf]:= 0; { buffer now available }
  483.          if f=$FFFF then f:= 0;
  484.        end
  485.      end;
  486.  
  487. @ Next we need a function to open an input file. Checks will be
  488.   made to verify that there are not too many include files open.
  489.  
  490. @p function open_fil (var f: word; name: String): boolean;
  491.      var
  492.        tf: word;
  493.      begin
  494.        open_fil:= false;
  495.        tf:= 1;
  496.        while (tf<=FileMax) and (file_prev[tf]<>0) do
  497.          incr(tf);
  498.        if tf>FileMax then
  499.          fatal_halt('@@i ',name,': no more than ',FileMax,' open files.');
  500.        assign(textf(tf),name);
  501.        SetTextBuf(textf(tf),Ptr(file_ptr[tf],128)^,buffer_size);
  502.        reset(textf(tf));
  503.        if IoResult=0 then begin
  504.          open_fil:= true;
  505.          if f=0 then f:= $FFFF;
  506.          file_prev[tf]:= f;
  507.          f:= tf
  508.        end
  509.      end;
  510.  
  511. @ @<Close all Files@>=
  512. while web_file>0 do close_fil(web_file);
  513. for ChgLevel:= 1 to ChgCnt do
  514.   while ChgFile[ChgLevel]>0 do close_fil(ChgFile[ChgLevel]);
  515.  
  516. @ The following code initializes the input buffers
  517. @<Local variables for init...@>=
  518. tf: word;
  519.  
  520. @ @<Set init...@>=
  521.   FileMax:= No_of_Files;
  522.   repeat
  523.     file_ptr[1]:= malloc(((buffer_size+128) shr 4) *FileMax);
  524.     if file_ptr[1]=0 then decr(FileMax)
  525.   until (file_ptr[1]<>0) or (FileMax<2);
  526.   if file_ptr[1]=0 then
  527.     fatal_halt('No memory for the webfile and a changefile.');
  528.   tf:= 2;
  529.   while tf<=No_of_Files do begin
  530.     file_ptr[tf]:= file_ptr[tf-1]+(buffer_size+128) shr 4;
  531.     incr(tf)
  532.   end;
  533.  
  534. @ We need a procedure to force the extension in a filename.
  535. @<Inline proc...@>=
  536.    function ForceExtension (FName, FExt : String): String;
  537.    {-Return a pathname with the specified extension attached}
  538.    var
  539.      i,DotPos,BackSlashPos: byte;
  540.    begin
  541.      DotPos := 0;
  542.      for i := 1 to _Length(FName) do begin
  543.        if FName[I]='.' then DotPos := i;
  544.        if FName[i]='\' then BackSlashPos:= i;
  545.      end;
  546.      if DotPos>BackSlashPos then
  547.        ForceExtension:= _copy(FName,1,DotPos)+FExt
  548.      else
  549.        ForceExtension := FName+'.'+FExt;
  550.    end;
  551.  
  552. @ Now we open the |web_file| and the |ChgFile|s if present.
  553. @p procedure open_input; {prepare to read |web_file| and |ChgFile|}
  554. var
  555.   fn: String;
  556.   tf,pc: word;
  557. begin
  558.   for tf:= 1 to No_of_Files do
  559.     file_prev[tf]:= 0;
  560.   web_file:= 0;
  561.   if not open_fil(web_file,Parameter(1)) then
  562.     if not open_fil(web_file,ForceExtension(Parameter(1),'WEB')) then
  563.       fatal_halt('WEB file not found');
  564.   fn:= Parameter(2); if (fn='') or (fn='*') then fn:= Parameter(1);
  565.   ChgCnt:= 0; pc:= 2;
  566.   while fn<>'' do begin
  567.     if open_fil(ChgFile[ChgCnt+1],fn) then
  568.       incr(ChgCnt)
  569.     else
  570.       if open_fil(ChgFile[ChgCnt+1],ForceExtension(fn,'CHG')) then
  571.         incr(ChgCnt)
  572.       else
  573.         print_ln('CHG file ',fn,' not found.');
  574.     incr(pc);
  575.     fn:= Parameter(pc)
  576.   end
  577. end;
  578. @z
  579. ────────────────────────────────────────────────────────────────
  580. @x l.549 m.25
  581. @!Pascal_file: text_file;
  582. @y
  583. @!Pascal_file: text_file;
  584. @!Pascal_buffer: word;
  585. @z
  586. ────────────────────────────────────────────────────────────────
  587. @x l.558 m.26
  588. @<Set init...@>=
  589. rewrite(Pascal_file); rewrite(pool);
  590. @y
  591. @<Close all...@>=
  592. @!Close(Pascal_file);
  593. @
  594. @<Set init...@>=
  595. Pascal_buffer:= malloc(8192 shr 4);
  596. if Pascal_buffer=0 then fatal_halt('No buffer for the pascal file.');
  597. assign(Pascal_file,ForceExtension(Parameter(1),'PAS'));
  598. SetTextBuf(Pascal_file,Ptr(Pascal_buffer,0)^,8192);
  599. rewrite(Pascal_file);
  600. if IoResult>0 then fatal_halt('Unable to create ',ForceExtension(Parameter(1),'PAS'));
  601. assign(pool,Parameter(1)+'.POO'); rewrite(pool);
  602. @z
  603. ────────────────────────────────────────────────────────────────
  604. @x l.579 m.28
  605. @p function input_ln(var f:text_file):boolean;
  606.   {inputs a line or returns |false|}
  607. var final_limit:0..buf_size; {|limit| without trailing blanks}
  608. begin limit:=0; final_limit:=0;
  609. if eof(f) then input_ln:=false
  610. else  begin while not eoln(f) do
  611.     begin buffer[limit]:=xord[f^]; get(f);
  612.     incr(limit);
  613.     if buffer[limit-1]<>" " then final_limit:=limit;
  614.     if limit=buf_size then
  615.       begin while not eoln(f) do get(f);
  616.       decr(limit); {keep |buffer[buf_size]| empty}
  617.       if final_limit>limit then final_limit:=limit;
  618.       print_nl('! Input line too long'); loc:=0; error;
  619. @.Input line too long@>
  620.       end;
  621.     end;
  622.   read_ln(f); limit:=final_limit; input_ln:=true;
  623.   end;
  624. end;
  625. @y
  626. @p function input_ln (var f: word):boolean;
  627.  label
  628.    new_file;
  629.  var
  630.    s: String;
  631.    fileend: boolean;
  632.    i: byte;
  633.  
  634.      procedure open_include;
  635.      var
  636.        i: byte;
  637.        fn: String;
  638.      begin
  639.        i:= 4;
  640.        while (i<=Length(s)) and (s[i]<>' ') do incr(i);
  641.        byte(fn[0]):= i-4;
  642.        move(s[4],fn[1],Length(fn));
  643.        if not open_fil(f,fn) then
  644.          if not open_fil(f,fn+'.CHI') then
  645.            if not open_fil(f,fn+'.CHG') then
  646.              fatal_halt('@@i ',fn,': Include file not found.')
  647.      end;
  648.  
  649.  begin new_file:
  650.    limit:= 0;
  651.    fileend:= eof(textf(f));
  652.    if IoResult>0 then fileend:= true;
  653.    if fileend then begin
  654.      close_fil(f);
  655.      if f>0 then
  656.        goto new_file
  657.      else
  658.        input_ln:= false
  659.    end else begin
  660.      readln(textf(f),s);
  661.      limit:= byte(s[0]);
  662.      if (limit>3) and (s[1]='@@') and (s[2]='i') and (s[3]=' ') then begin
  663.        open_include;
  664.        goto new_file
  665.      end;
  666.      while (limit>0) and (s[limit]=' ') do decr(limit);
  667.      for i:= 1 to limit do buffer[i-1]:= xord[s[i]];
  668.      input_ln:=true;
  669.    end;
  670.  end;
  671. @z
  672. ────────────────────────────────────────────────────────────────
  673. @x l.651 m.32
  674. @<Print error location based on input buffer@>=
  675. begin if changing then print('. (change file ')@+else print('. (');
  676. print_ln('l.', line:1, ')');
  677. @y
  678. @<Print error location based on input buffer@>=
  679. begin
  680.   if ChgLevel>0 then
  681.     print('. (change file #',ChgAct[ChgLevel],
  682.           ' l.',ChgLine[ChgAct[ChgLevel]]:1)
  683.   else
  684.     print('. (l.', line:1);
  685.   print_ln(')');
  686. @z
  687. ────────────────────────────────────────────────────────────────
  688. @x l.685 m.34
  689. @d fatal_error(#)==begin new_line; print(#); error; mark_fatal; jump_out;
  690.   end
  691.  
  692. @<Error handling...@>=
  693. procedure jump_out;
  694. begin goto end_of_TANGLE;
  695. end;
  696. @y
  697. @d fatal_error(#)==begin new_line; print(#); error; mark_fatal; Halt(history) end
  698. @d fatal_halt(#)==begin new_line; print(#); mark_fatal; Halt(history) end
  699. @z
  700. ────────────────────────────────────────────────────────────────
  701. @x l.698 m.35
  702. @d confusion(#)==fatal_error('! This can''t happen (',#,')')
  703. @.This can't happen@>
  704.  
  705. @ An overflow stop occurs if \.{TANGLE}'s tables aren't large enough.
  706.  
  707. @d overflow(#)==fatal_error('! Sorry, ',#,' capacity exceeded')
  708. @.Sorry, x capacity exceeded@>
  709. @y
  710. @p procedure confusion(s: String);
  711. begin fatal_error('! This can''t happen (',s,')')
  712. @.This can't happen@>
  713. end;
  714.  
  715. @ An overflow stop occurs if \.{TANGLE}'s tables aren't large enough.
  716.  
  717. @p procedure overflow(s: String);
  718. begin fatal_error('! Sorry, ',s,' capacity exceeded')
  719. @.Sorry, x capacity exceeded@>
  720. end;
  721. @z
  722. ────────────────────────────────────────────────────────────────
  723. @x l.736 m.38
  724. @d ww=2 {we multiply the byte capacity by approximately this amount}
  725. @d zz=3 {we multiply the token capacity by approximately this amount}
  726. @y
  727. @d ww=16 {we multiply the byte capacity by approximately this amount}
  728. @d zz=16 {we multiply the token capacity by approximately this amount}
  729. @z
  730. ────────────────────────────────────────────────────────────────
  731. @x l.739 m.38
  732. @<Globals...@>=
  733. @!byte_mem: packed array [0..ww-1,0..max_bytes] of ASCII_code;
  734.   {characters of names}
  735. @y
  736. @ @<Set init...@>=
  737. if (ww<>16) then
  738.   fatal_halt('! ww must be 16 (segment size).');
  739. free:= mavail;
  740. while (max_bytes+max_toks>free)
  741.   and (max_bytes>min_bytes) and (max_toks>min_toks) do begin
  742.     decr(max_bytes,step_bytes);
  743.     decr(max_toks,step_toks)
  744. end;
  745. byte_seg:= malloc(max_bytes);
  746. tok_seg:= malloc(max_toks);
  747. if byte_seg=0 then
  748.   fatal_halt('! no memory for byte_mem');
  749. if tok_seg=0 then
  750.   fatal_halt('! no memory for tok_mem');
  751.  
  752. @ @<Inline...@>=
  753.   function bytem (s,o: word): Pointer;@/
  754.   Asm(pop dx/
  755.       pop ax/
  756.       add dx,[>byte_seg]);
  757.  
  758. @ @d byte_mem[#]==ASCII_code(bytem(#)^)
  759. @ @<Globals...@>=
  760. @t\hskip1em@>@!byte_seg: word;
  761. @!free: word;
  762. @z
  763. ────────────────────────────────────────────────────────────────
  764. @x l.742 m.38
  765. @!tok_mem: packed array [0..zz-1,0..max_toks] of eight_bits; {tokens}
  766. @y
  767. @ @<Set init...@>=
  768. if (zz<>16) then
  769.   fatal_halt('! zz must be 16 (segment size).');
  770.  
  771. @ @<Inline...@>=
  772.   function tokm (s,o: word): Pointer;@/
  773.   Asm(pop dx/
  774.       pop ax/
  775.       add dx,[>tok_seg]);
  776.  
  777. @ @d tok_mem[#]==eight_bits(tokm(#)^)
  778. @ @<Globals...@>=
  779. @t\hskip1em@>@!tok_seg: word;
  780. @z
  781. ────────────────────────────────────────────────────────────────
  782. @x l.788 m.40
  783. @!byte_ptr:array [0..ww-1] of 0..max_bytes;
  784.   {first unused position in |byte_mem|}
  785. @!pool_check_sum:integer; {sort of a hash for the whole string pool}
  786. @y
  787. @!byte_ptr:array [0..ww-1] of 0..max_max_bytes;
  788.   {first unused position in |byte_mem|}
  789. @!pool_check_sum:LongInt; {sort of a hash for the whole string pool}
  790. @z
  791. ────────────────────────────────────────────────────────────────
  792. @x l.828 m.44
  793. @t\hskip1em@>@!tok_ptr:array[0..zz-1] of 0..max_toks;
  794.   {first unused position in a given segment of |tok_mem|}
  795. @y
  796. @t\hskip1em@>@!tok_ptr:array[0..zz-1] of 0..max_max_toks;
  797.   {first unused position in a given segment of |tok_mem|}
  798. @z
  799. ────────────────────────────────────────────────────────────────
  800. @x l.831 m.44
  801. stat @!max_tok_ptr:array[0..zz-1] of 0..max_toks;
  802.   {largest values assumed by |tok_ptr|}
  803. tats
  804. @y
  805. stat @!max_tok_ptr:array[0..zz-1] of 0..max_max_toks;
  806.   {largest values assumed by |tok_ptr|}
  807. tats
  808. @z
  809. ────────────────────────────────────────────────────────────────
  810. @x l.863 m.47
  811. like simple identifiers, their |equiv| value points to the replacement text.
  812. @y
  813. like simple identifiers, their |equiv| value points to the replacement text.
  814.  
  815. \yskip\hang |array_type| identifiers have been defined to be array-type macros;
  816. they are like parametric identifiers but the macro parameters and
  817. arguments are enclosed in square brackets.
  818. @z
  819. ────────────────────────────────────────────────────────────────
  820. @x l.868 m.47
  821. @d parametric=3 {parametric macros have |parametric| ilk}
  822. @y
  823. @d parametric=3 {parametric macros have |parametric| ilk}
  824. @d array_type=4 {array-type macros have |array_type| ilk}
  825. @z
  826. ────────────────────────────────────────────────────────────────
  827. @x l.890 m.49
  828. var k:0..max_bytes; {index into |byte_mem|}
  829. @y
  830. var k:0..max_max_bytes; {index into |byte_mem|}
  831. @z
  832. ────────────────────────────────────────────────────────────────
  833. @x l.961 m.53
  834. @!k:0..max_bytes; {index into |byte_mem|}
  835. @y
  836. @!k:0..max_max_bytes; {index into |byte_mem|}
  837. @z
  838. ────────────────────────────────────────────────────────────────
  839. @x l.979 m.54
  840. h:=buffer[id_first]; i:=id_first+1;
  841. while i<id_loc do
  842.   begin h:=(h+h+buffer[i]) mod hash_size; incr(i);
  843.   end
  844. @y
  845. Asm(_cld/
  846.     _mov bx,>hash_size/
  847.     _xor ax,ax/
  848.     _xor dx,dx/
  849.     _mov si,>buffer/
  850.     _add si,[>id_first]/
  851.     _mov cx,[bp+<l]/
  852. lp1:_shl dx,1/
  853.     _lodsb/
  854.     _add dx,ax/
  855. lp2:_sub dx,bx/
  856.     _jge lp2/
  857.     _add dx,bx/
  858.     _loop lp1/
  859.     _mov [bp+<h],dx);
  860. @z
  861. ────────────────────────────────────────────────────────────────
  862. @x l.1024 m.58          - make underscore significant
  863.   begin if buffer[i]<>"_" then
  864.     begin if buffer[i]>="a" then chopped_id[s]:=buffer[i]-@'40
  865.     else chopped_id[s]:=buffer[i];
  866.     h:=(h+h+chopped_id[s]) mod hash_size; incr(s);
  867.     end;
  868.   incr(i);
  869. @y
  870.   begin
  871.     chopped_id[s]:=buffer[i];
  872.     h:=(h+h+chopped_id[s]) mod hash_size; incr(s);
  873.     incr(i);
  874. @z
  875. ────────────────────────────────────────────────────────────────
  876. @x l.1096 m.63          - make underscore significant
  877.   if c<>"_" then
  878.     begin if c>="a" then c:=c-@'40; {merge lowercase with uppercase}
  879.     if chopped_id[s]<>c then goto not_found;
  880.     incr(s);
  881.     end;
  882. @y
  883.   if chopped_id[s]<>c then goto not_found;
  884.   incr(s);
  885. @z
  886. ────────────────────────────────────────────────────────────────
  887. @x l.1168 m.66
  888. @!k:0..max_bytes; {index into |byte_mem|}
  889. @y
  890. @!k:0..max_max_bytes; {index into |byte_mem|}
  891. @z
  892. ────────────────────────────────────────────────────────────────
  893. @x l.1222 m.69
  894. @!k:0..max_bytes; {index into |byte_mem|}
  895. @y
  896. @!k:0..max_max_bytes; {index into |byte_mem|}
  897. @z
  898. ────────────────────────────────────────────────────────────────
  899. @x l.1324 m.72          - implement an inline assembler
  900. @d join=@'177 {ASCII delete will not appear}
  901. @y
  902. @d join=@'177 {ASCII delete will not appear}
  903. @d asm_start=@'16 {ASCII SO will not appear}
  904. @d asm_end=@'17 {ASCII SI will not appear}
  905. @z
  906. ────────────────────────────────────────────────────────────────
  907. @x l.1332 m.73
  908. tok_mem[z,tok_ptr[z]]:=x div@'400; {this could be done by a shift command}
  909. tok_mem[z,tok_ptr[z]+1]:=x mod@'400; {this could be done by a logical and}
  910. @y
  911. tok_mem[z,tok_ptr[z]]:=hi(x); tok_mem[z,tok_ptr[z]+1]:=lo(x);
  912. @z
  913. ────────────────────────────────────────────────────────────────
  914. @x l.1495 m.85
  915.   begin if ilk[cur_name]=parametric then
  916. @y
  917.   begin if ilk[cur_name]>=parametric then
  918. @z
  919. ────────────────────────────────────────────────────────────────
  920. @x l.1527 m.86
  921. @!cur_val:integer; {additional information corresponding to output token}
  922. @y
  923. @!cur_val:LongInt; {additional information corresponding to output token}
  924. @z
  925. ────────────────────────────────────────────────────────────────
  926. @x l.1536 m.87
  927. @!k:0..max_bytes; {index into |byte_mem|}
  928. @y
  929. @!k:0..max_max_bytes; {index into |byte_mem|}
  930. @z
  931. ────────────────────────────────────────────────────────────────
  932. @x l.1546 m.87          - implement an inline assembler
  933. a:=tok_mem[zo,cur_byte]; incr(cur_byte);
  934. @y
  935. a:=tok_mem[zo,cur_byte]; incr(cur_byte);
  936. case a of
  937.   asm_start,asm_end: goto found;
  938. end;
  939. @z
  940. ────────────────────────────────────────────────────────────────
  941. @x l.1579 m.89
  942. numeric: begin cur_val:=equiv[a]-@'100000; a:=number;
  943. @y
  944. numeric: begin cur_val:=LongInt(equiv[a])-@'100000; a:=number;
  945. @z
  946. ────────────────────────────────────────────────────────────────
  947. @x l.1583 m.89
  948. parametric: begin @<Put a parameter on the parameter stack,
  949. @y
  950. parametric,array_type: begin @<Put a parameter on the parameter stack,
  951. @z
  952. ────────────────────────────────────────────────────────────────
  953. @x l.1598 m.90
  954. @<Put a parameter...@>=
  955. while (cur_byte=cur_end)and(stack_ptr>0) do pop_level;
  956. if (stack_ptr=0)or(tok_mem[zo,cur_byte]<>"(") then
  957.   begin print_nl('! No parameter given for '); print_id(a); error;
  958. @.No parameter given for macro@>
  959.   goto restart;
  960.   end;
  961. @<Copy the parameter into |tok_mem|@>;
  962. @y
  963. @d NoParam(#)== begin print_nl(#); print_id(a); error; goto restart end
  964. @<Put a parameter...@>=
  965. while (cur_byte=cur_end)and(stack_ptr>0) do pop_level;
  966. if stack_ptr=0 then NoParam('! No parameter given for ');
  967. case ilk[a] of
  968.   parametric: if tok_mem[zo,cur_byte]="(" then begin
  969.                 @<Copy the parameter into |tok_mem|@>
  970.               end else
  971.                 NoParam('! No parameter given for ');
  972.   array_type: if tok_mem[zo,cur_byte]="[" then begin
  973.                 @<Copy the array parameter into |tok_mem|@>
  974.               end else
  975.                 NoParam('! No array parameter given for ');
  976. end;
  977. @z
  978. ────────────────────────────────────────────────────────────────
  979. @x l.1783 m.95
  980. @!out_val,@!out_app:integer; {pending values}
  981. @y
  982. @!out_val,@!out_app:LongInt; {pending values}
  983. @!assembling: boolean; {true, when parsing/expanding assembler text}
  984. @z
  985. ────────────────────────────────────────────────────────────────
  986. @x l.1791 m.96
  987. out_state:=misc; out_ptr:=0; break_ptr:=0; semi_ptr:=0; out_buf[0]:=0; line:=1;
  988. @y
  989. out_state:=misc; out_ptr:=0; break_ptr:=0; semi_ptr:=0; out_buf[0]:=0; line:=1;
  990. assembling:= false;
  991. @z
  992. ────────────────────────────────────────────────────────────────
  993. @x l.1799 m.97
  994. @d check_break==if out_ptr>line_length then flush_buffer
  995. @y
  996. @d check_break==if out_ptr>line_length then flush_buffer
  997. @d im_break==begin
  998.                break_ptr:= out_ptr;
  999.                semi_ptr:= 0;
  1000.                flush_buffer
  1001.              end
  1002. @z
  1003. ────────────────────────────────────────────────────────────────
  1004. @x l.1806 m.97          -implement an inline assembler
  1005. for k:=1 to break_ptr do write(Pascal_file,xchr[out_buf[k-1]]);
  1006. write_ln(Pascal_file); incr(line);
  1007. if line mod 100 = 0 then
  1008.   begin print('.');
  1009.   if line mod 500 = 0 then print(line:1);
  1010.   update_terminal; {progress report}
  1011.   end;
  1012. if break_ptr<out_ptr then
  1013.   begin if out_buf[break_ptr]=" " then
  1014.     begin incr(break_ptr); {drop space at break}
  1015.     if break_ptr>b then b:=break_ptr;
  1016.     end;
  1017.   for k:=break_ptr to out_ptr-1 do out_buf[k-break_ptr]:=out_buf[k];
  1018.   end;
  1019. out_ptr:=out_ptr-break_ptr; break_ptr:=b-break_ptr; semi_ptr:=0;
  1020. if out_ptr>line_length then
  1021.   begin err_print('! Long line must be truncated'); out_ptr:=line_length;
  1022. @.Long line must be truncated@>
  1023.   end;
  1024. @y
  1025. if assembling then begin
  1026.   for k:= 1 to out_ptr do
  1027.     if not FeedAsm(xchr[out_buf[k-1]]) then
  1028.       overflow('inline input');
  1029.   out_ptr:= 0
  1030. end else begin
  1031.   for k:= 1 to break_ptr do write(Pascal_file,xchr[out_buf[k-1]]);
  1032.   write_ln(Pascal_file); incr(line);
  1033.   if line mod 100 = 0 then
  1034.     begin print('.');
  1035.     if line mod 500 = 0 then print(line:1);
  1036.     update_terminal; {progress report}
  1037.     end;
  1038.   if break_ptr<out_ptr then
  1039.     begin if out_buf[break_ptr]=" " then
  1040.       begin incr(break_ptr); {drop space at break}
  1041.       if break_ptr>b then b:=break_ptr;
  1042.       end;
  1043.       move(out_buf[break_ptr],out_buf[0],out_ptr-break_ptr);
  1044.     end;
  1045.   decr(out_ptr,break_ptr); break_ptr:=b-break_ptr; semi_ptr:=0;
  1046.   if out_ptr>line_length then
  1047.     begin err_print('! Long line must be truncated'); out_ptr:=line_length;
  1048.   @.Long line must be truncated@>
  1049.     end;
  1050. end;
  1051. @z
  1052. ────────────────────────────────────────────────────────────────
  1053. @x l.1839 m.99
  1054. @p procedure app_val(@!v:integer); {puts |v| into buffer, assumes |v>=0|}
  1055. @y
  1056. @p procedure app_val(@!v:LongInt); {puts |v| into buffer, assumes |v>=0|}
  1057. @z
  1058. ────────────────────────────────────────────────────────────────
  1059. @x l.1881 m.101
  1060. if t<>misc then for k:=1 to v do app(out_contrib[k])
  1061. @y
  1062. if t<>misc then begin
  1063.   move(out_contrib,out_buf[out_ptr],v); incr(out_ptr,v)
  1064.   end
  1065. @z
  1066. ────────────────────────────────────────────────────────────────
  1067. @x l.1930 m.105
  1068.  (((out_contrib[1]="D")and(out_contrib[2]="I")and(out_contrib[3]="V")) or@|
  1069.  ((out_contrib[1]="M")and(out_contrib[2]="O")and(out_contrib[3]="D")) ))or@|
  1070. @y
  1071.  (((out_contrib[1]="d")and(out_contrib[2]="i")and(out_contrib[3]="v")) or@|
  1072.  ((out_contrib[1]="a")and(out_contrib[2]="n")and(out_contrib[3]="d")) or@|
  1073.  ((out_contrib[1]="s")and(out_contrib[2]="h")and
  1074.   ((out_contrib[3]="l")or(out_contrib[3]="r"))) or@|
  1075.  ((out_contrib[1]="m")and(out_contrib[2]="o")and(out_contrib[3]="d")) ))or@|
  1076. @z
  1077. ────────────────────────────────────────────────────────────────
  1078. @x l.1941 m.106
  1079. @p procedure send_sign(@!v:integer);
  1080. @y
  1081. @p procedure send_sign(@!v:LongInt);
  1082. @z
  1083. ────────────────────────────────────────────────────────────────
  1084. @x l.1959 m.107
  1085. @p procedure send_val(@!v:integer); {output the (signed) value |v|}
  1086. @y
  1087. @p procedure send_val(@!v:LongInt); {output the (signed) value |v|}
  1088. var potcnt: byte;
  1089. @z
  1090. ────────────────────────────────────────────────────────────────
  1091. @x l.1995 m.110         - optimize DIV and MOD
  1092. @ @<If previous output was \.{DIV}...@>=
  1093. if (out_ptr=break_ptr+3)or
  1094.  ((out_ptr=break_ptr+4)and(out_buf[break_ptr]=" ")) then
  1095. @^uppercase@>
  1096.   if ((out_buf[out_ptr-3]="D")and(out_buf[out_ptr-2]="I")and
  1097.     (out_buf[out_ptr-1]="V"))or @/
  1098.      ((out_buf[out_ptr-3]="M")and(out_buf[out_ptr-2]="O")and
  1099.     (out_buf[out_ptr-1]="D")) then@/ goto bad_case
  1100. @y
  1101. @ @<Typed constants...@>=
  1102. modopt: boolean = false;
  1103. divopt: boolean = false;
  1104. InsComments: boolean = false;
  1105.  
  1106. @ @<Local variables for init...@>=
  1107. pi,pp: word;
  1108. s: String;
  1109. @ @<Set init...@>=
  1110.    for pi:= 1 to ParamCount do begin
  1111.      s:= ParamStr(pi);
  1112.      pp:= pos('-',s);
  1113.      if pp=0 then pp:= pos('/',s);
  1114.      if pp>0 then case UpCase(s[pp+1]) of
  1115.        'D': divopt:= true;
  1116.        'M': modopt:= true;
  1117.        'C': InsComments:= true;
  1118.      end
  1119.    end;
  1120.  
  1121. @ @<If previous output was \.{DIV}...@>=
  1122. begin if (out_ptr=break_ptr+3)or
  1123.  ((out_ptr=break_ptr+4)and(out_buf[break_ptr]=" ")) then
  1124. @^uppercase@>
  1125.  begin
  1126.   if ((out_buf[out_ptr-3]="d")and(out_buf[out_ptr-2]="i")and
  1127.     (out_buf[out_ptr-1]="v") and divopt) then begin
  1128.       if (v>0) and (v<257) and (v and (v-1)=0) then begin
  1129.         potcnt:= 0;
  1130.         while lo(v) and 1=0 do begin
  1131.           incr(potcnt);
  1132.           v:= v shr 1
  1133.         end;
  1134.         v:= potcnt;
  1135.         out_buf[out_ptr-3]:= "s";
  1136.         out_buf[out_ptr-2]:= "h";
  1137.         out_buf[out_ptr-1]:= "r";
  1138.       end;
  1139.       goto bad_case
  1140.     end;
  1141.     if ((out_buf[out_ptr-3]="m")and(out_buf[out_ptr-2]="o")and
  1142.     (out_buf[out_ptr-1]="d")and modopt) then begin
  1143.       if (v>0) and (v and (v-1)=0) then begin
  1144.         Dec(v);
  1145.         out_buf[out_ptr-3]:= "a";
  1146.         out_buf[out_ptr-2]:= "n";
  1147.         out_buf[out_ptr-1]:= "d";
  1148.       end;
  1149.       goto bad_case
  1150.     end;
  1151.     if ((out_buf[out_ptr-3]="s")and(out_buf[out_ptr-2]="h")and
  1152.     (out_buf[out_ptr-1]="l")) or @/
  1153.      ((out_buf[out_ptr-3]="s")and(out_buf[out_ptr-2]="h")and
  1154.     (out_buf[out_ptr-1]="r")) then@/ goto bad_case
  1155.  end
  1156. end
  1157. @z
  1158. ────────────────────────────────────────────────────────────────
  1159. @x l.2042 m.113
  1160.   @!j:0..max_bytes; {index into |byte_mem|}
  1161.   @!w:0..ww-1; {segment of |byte_mem|}
  1162.   @!n:integer; {number being scanned}
  1163. @y
  1164.   @!j:0..max_max_bytes; {index into |byte_mem|}
  1165.   @!w:0..ww-1; {segment of |byte_mem|}
  1166.   @!n:LongInt; {number being scanned}
  1167.   @!outind: word;
  1168. @z
  1169. ────────────────────────────────────────────────────────────────
  1170. @x l.2059 m.113         - implement an inline assembler
  1171.   verbatim: @<Send verbatim string@>;
  1172. @y
  1173.   verbatim: @<Send verbatim string@>;
  1174.   asm_start: if assembling then
  1175.                err_print('! Already assembling')
  1176. @.Already assembling@>
  1177.              else begin
  1178.                SetUpAsm;
  1179.                im_break;
  1180.                assembling:= true
  1181.              end;
  1182.   asm_end:   if assembling then begin
  1183.                send_out(frac,0); {tss, tss}
  1184.                im_break;
  1185.                assembling:= false;
  1186.                if not DoAsm(InsComments) then
  1187.                  mark_harmless;
  1188.  
  1189.                for outind:= 0 to ObjSize-1 do
  1190.                  case byte(TextArray[outind]) of
  1191.                    13: im_break;
  1192.                    10: do_nothing;
  1193.                  else
  1194.                    app(byte(TextArray[outind]))
  1195.                  end
  1196.              end else
  1197.                err_print('! Not in assembler mode');
  1198. @.Not in assembler mode@>
  1199. @z
  1200. ────────────────────────────────────────────────────────────────
  1201. @x l.2070 m.114
  1202. and_sign: begin out_contrib[1]:="A"; out_contrib[2]:="N"; out_contrib[3]:="D";
  1203. @y
  1204. and_sign: begin out_contrib[1]:="a"; out_contrib[2]:="n"; out_contrib[3]:="d";
  1205. @z
  1206. ────────────────────────────────────────────────────────────────
  1207. @x l.2074 m.114
  1208. not_sign: begin out_contrib[1]:="N"; out_contrib[2]:="O"; out_contrib[3]:="T";
  1209. @y
  1210. not_sign: begin out_contrib[1]:="n"; out_contrib[2]:="o"; out_contrib[3]:="t";
  1211. @z
  1212. ────────────────────────────────────────────────────────────────
  1213. @x l.2077 m.114
  1214. set_element_sign: begin out_contrib[1]:="I"; out_contrib[2]:="N";
  1215. @y
  1216. set_element_sign: begin out_contrib[1]:="i"; out_contrib[2]:="n";
  1217. @z
  1218. ────────────────────────────────────────────────────────────────
  1219. @x l.2080 m.114
  1220. or_sign: begin out_contrib[1]:="O"; out_contrib[2]:="R"; send_out(ident,2);
  1221. @y
  1222. or_sign: begin out_contrib[1]:="o"; out_contrib[2]:="r"; send_out(ident,2);
  1223. @z
  1224. ────────────────────────────────────────────────────────────────
  1225. @x l.2124 m.116
  1226. @d up_to(#)==#-24,#-23,#-22,#-21,#-20,#-19,#-18,#-17,#-16,#-15,#-14,
  1227.   #-13,#-12,#-11,#-10,#-9,#-8,#-7,#-6,#-5,#-4,#-3,#-2,#-1,#
  1228. @y
  1229. @z
  1230. ────────────────────────────────────────────────────────────────
  1231. @x l.2128 m.116
  1232. "A",up_to("Z"): begin out_contrib[1]:=cur_char; send_out(ident,1);
  1233.   end;
  1234. "a",up_to("z"): begin out_contrib[1]:=cur_char-@'40; send_out(ident,1);
  1235.   end;
  1236. @y
  1237. "A".."Z","a".."z": begin out_contrib[1]:=cur_char; send_out(ident,1); end;
  1238. @z
  1239. ────────────────────────────────────────────────────────────────
  1240. @x l.2132 m.116
  1241. identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
  1242.   while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
  1243.     begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
  1244.     if out_contrib[k]>="a" then out_contrib[k]:=out_contrib[k]-@'40
  1245.     else if out_contrib[k]="_" then decr(k);
  1246.     end;
  1247. @y
  1248. identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
  1249.   while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
  1250.     begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
  1251.     if out_contrib[k]="_" then decr(k);
  1252.     end;
  1253. @z
  1254. ────────────────────────────────────────────────────────────────
  1255. @x l.2289 m.124         - implement multiple change files
  1256. @ But first we need to consider the low-level routine |get_line|
  1257. that takes care of merging |change_file| into |web_file|. The |get_line|
  1258. procedure also updates the line numbers for error messages.
  1259.  
  1260. @<Globals...@>=
  1261. @!line:integer; {the number of the current line in the current file}
  1262. @!other_line:integer; {the number of the current line in the input file that
  1263.   is not currently being read}
  1264. @!temp_line:integer; {used when interchanging |line| with |other_line|}
  1265. @!limit:0..buf_size; {the last character position occupied in the buffer}
  1266. @!loc:0..buf_size; {the next character position to be read from the buffer}
  1267. @!input_has_ended: boolean; {if |true|, there is no more input}
  1268. @!changing: boolean; {if |true|, the current line is from |change_file|}
  1269.  
  1270. @ As we change |changing| from |true| to |false| and back again, we must
  1271. remember to swap the values of |line| and |other_line| so that the |err_print|
  1272. routine will be sure to report the correct line number.
  1273.  
  1274. @d change_changing==
  1275.   changing := not changing;
  1276.   temp_line:=other_line; other_line:=line; line:=temp_line
  1277.     {|line @t$\null\BA\null$@> other_line|}
  1278.  
  1279. @ When |changing| is |false|, the next line of |change_file| is kept in
  1280. |change_buffer[0..change_limit]|, for purposes of comparison with the next
  1281. line of |web_file|. After the change file has been completely input, we
  1282. set |change_limit:=0|, so that no further matches will be made.
  1283.  
  1284. @<Globals...@>=
  1285. @!change_buffer:array[0..buf_size] of ASCII_code;
  1286. @!change_limit:0..buf_size; {the last position occupied in |change_buffer|}
  1287. @y
  1288. @ But first we need to consider the low-level routine |get_line|
  1289. that takes care of merging |change_file| into |web_file|. The |get_line|
  1290. procedure also updates the line numbers for error messages.
  1291.  
  1292. @<Globals...@>=
  1293. @!line:word; {the number of the current line in the current file}
  1294. @!limit:0..buf_size; {the last character position occupied in the buffer}
  1295. @!loc:0..buf_size; {the next character position to be read from the buffer}
  1296. @!input_has_ended: boolean; {if |true|, there is no more input}
  1297.  
  1298. @ The next line of the |Chg_File|s is kept in
  1299. |ChgBuffer|, for purposes of comparison with the next
  1300. line of |web_file| or another |ChgFile|. After a change file
  1301. has been completely input, we set the corresponding |ChgLimit|
  1302. to zero, so that no further matches will be made.
  1303.  
  1304. @<Globals...@>=
  1305. @!ChgBuffer:array[1..ChangeMax] of array[0..buf_size] of ASCII_code;
  1306. @!ChgLimit: array[1..ChangeMax] of 0..buf_size; {the last positions occupied in |ChgBuffer|}
  1307. @!ChgLine: array[1..ChangeMax] of word; {the line numbers}
  1308. @z
  1309. ────────────────────────────────────────────────────────────────
  1310. @x l.2321 m.127         - implement multiple change files
  1311. @ Here's a simple function that checks if the two buffers are different.
  1312.  
  1313. @p function lines_dont_match:boolean;
  1314. label exit;
  1315. var k:0..buf_size; {index into the buffers}
  1316. begin lines_dont_match:=true;
  1317. if change_limit<>limit then return;
  1318. if limit>0 then
  1319.   for k:=0 to limit-1 do if change_buffer[k]<>buffer[k] then return;
  1320. lines_dont_match:=false;
  1321. exit: end;
  1322.  
  1323. @ Procedure |prime_the_change_buffer| sets |change_buffer| in preparation
  1324. for the next matching operation. Since blank lines in the change file are
  1325. not used for matching, we have |(change_limit=0)and not changing| if and
  1326. only if the change file is exhausted. This procedure is called only
  1327. when |changing| is true; hence error messages will be reported correctly.
  1328.  
  1329. @p procedure prime_the_change_buffer;
  1330. label continue, done, exit;
  1331. var k:0..buf_size; {index into the buffers}
  1332. begin change_limit:=0; {this value will be used if the change file ends}
  1333. @<Skip over comment lines in the change file; |return| if end of file@>;
  1334. @<Skip to the next nonblank line; |return| if end of file@>;
  1335. @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>;
  1336. exit: end;
  1337.  
  1338. @ While looking for a line that begins with \.{@@x} in the change file,
  1339. we allow lines that begin with \.{@@}, as long as they don't begin with
  1340. \.{@@y} or \.{@@z} (which would probably indicate that the change file is
  1341. fouled up).
  1342.  
  1343. @<Skip over comment lines in the change file...@>=
  1344. loop@+  begin incr(line);
  1345.   if not input_ln(change_file) then return;
  1346.   if limit<2 then goto continue;
  1347.   if buffer[0]<>"@@" then goto continue;
  1348.   if (buffer[1]>="X")and(buffer[1]<="Z") then
  1349.     buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
  1350.   if buffer[1]="x" then goto done;
  1351.   if (buffer[1]="y")or(buffer[1]="z") then
  1352.     begin loc:=2; err_print('! Where is the matching @@x?');
  1353. @.Where is the match...@>
  1354.     end;
  1355. continue: end;
  1356. done:
  1357.  
  1358. @ Here we are looking at lines following the \.{@@x}.
  1359.  
  1360. @<Skip to the next nonblank line...@>=
  1361. repeat incr(line);
  1362.   if not input_ln(change_file) then
  1363.     begin err_print('! Change file ended after @@x');
  1364. @.Change file ended...@>
  1365.     return;
  1366.     end;
  1367. until limit>0;
  1368. @y
  1369. @ Here's a simple function that checks |buffer| doesn't match a |ChgBuffer|.
  1370.  
  1371. @p function lines_dont_match(ci: word):boolean;
  1372. label exit;
  1373. var k:0..buf_size; {index into the buffers}
  1374. begin lines_dont_match:=true;
  1375. if ChgLimit[ci]<>limit then return;
  1376. if limit>0 then
  1377.   for k:=0 to limit-1 do if ChgBuffer[ci][k]<>buffer[k] then return;
  1378. lines_dont_match:=false;
  1379. exit: end;
  1380.  
  1381. @ Procedure |prime_the_change_buffer| sets a |ChgBuffer| in preparation
  1382. for the next matching operation. Since blank lines in the change files are
  1383. not used for matching, we have |(ChgLimit=0)and not changing| if and
  1384. only if the change file is exhausted. This procedure is called only
  1385. when |changing| is true; hence error messages will be reported correctly.
  1386.  
  1387. @p procedure prime_the_change_buffer (ci:word);
  1388. label exit;
  1389. var k:0..buf_size; {index into the buffers}
  1390. begin ChgLimit[ci]:=0; {this value will be used if the change file ends}
  1391. @<Skip over comment lines in the change file; |return| if end of file@>;
  1392. @<Skip to the next nonblank line; |return| if end of file@>;
  1393. ChgLimit[ci]:= limit;
  1394. move(buffer,ChgBuffer[ci],limit);
  1395. exit: end;
  1396.  
  1397. @ While looking for a line that begins with \.{@@x} in the change file,
  1398. we allow lines that begin with \.{@@}, as long as they don't begin with
  1399. \.{@@y} or \.{@@z} (which would probably indicate that the change file is
  1400. fouled up).
  1401.  
  1402. @<Skip over comment lines in the change file...@>=
  1403. repeat
  1404.   incr(ChgLine[ci]);
  1405.   if not input_ln(ChgFile[ci]) then return;
  1406.   if (limit>=2) and (buffer[0]="@@") then
  1407.     case buffer[1] of "Y","y","Z","z":
  1408.     begin loc:=2; err_print('! Where is the matching @@x?');
  1409. @.Where is the match...@>
  1410.     end end;
  1411. until (limit>=2) and (buffer[0]="@@")
  1412.   and ((buffer[1]="X") or (buffer[1]="x"));
  1413.  
  1414. @ Here we are looking at lines following the \.{@@x}.
  1415.  
  1416. @<Skip to the next nonblank line...@>=
  1417. repeat incr(ChgLine[ci]);
  1418.   if not input_ln(ChgFile[ci]) then
  1419.     begin err_print('! Change file #',ci,' ended after @@x');
  1420. @.Change file ended...@>
  1421.     return;
  1422.     end;
  1423. until limit>0;
  1424. @z
  1425. ────────────────────────────────────────────────────────────────
  1426. @x l.2379 m.131         - implement multiple change files
  1427. @ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>=
  1428. begin change_limit:=limit;
  1429. if limit>0 then for k:=0 to limit-1 do change_buffer[k]:=buffer[k];
  1430. end
  1431.  
  1432. @ The following procedure is used to see if the next change entry should
  1433. go into effect; it is called only when |changing| is false.
  1434. The idea is to test whether or not the current
  1435. contents of |buffer| matches the current contents of |change_buffer|.
  1436. If not, there's nothing more to do; but if so, a change is called for:
  1437. All of the text down to the \.{@@y} is supposed to match. An error
  1438. message is issued if any discrepancy is found. Then the procedure
  1439. prepares to read the next line from |change_file|.
  1440.  
  1441. @p procedure check_change; {switches to |change_file| if the buffers match}
  1442. label exit;
  1443. var n:integer; {the number of discrepancies found}
  1444. @!k:0..buf_size; {index into the buffers}
  1445. begin if lines_dont_match then return;
  1446. n:=0;
  1447. loop@+  begin change_changing; {now it's |true|}
  1448.   incr(line);
  1449.   if not input_ln(change_file) then
  1450.     begin err_print('! Change file ended before @@y');
  1451. @.Change file ended...@>
  1452.     change_limit:=0;  change_changing; {|false| again}
  1453.     return;
  1454.     end;
  1455.   @<If the current line starts with \.{@@y},
  1456.     report any discrepancies and |return|@>;
  1457.   @<Move |buffer| and |limit|...@>;
  1458.   change_changing; {now it's |false|}
  1459.   incr(line);
  1460.   if not input_ln(web_file) then
  1461.     begin err_print('! WEB file ended during a change');
  1462. @.WEB file ended...@>
  1463.     input_has_ended:=true; return;
  1464.     end;
  1465.   if lines_dont_match then incr(n);
  1466.   end;
  1467. exit: end;
  1468. @y
  1469. @ The following procedure is used to see if the next change entries should
  1470. go into effect. The idea is to test whether or not the current
  1471. contents of |buffer| matches the current contents of one |ChgBuffer|.
  1472. If not, there's nothing more to do; but if so, a change is called for:
  1473. All of the text down to the \.{@@y} is supposed to match. An error
  1474. message is issued if any discrepancy is found. Then the procedure
  1475. prepares to read the next line from |ChgFile|.
  1476.  
  1477. @p procedure check_change (newch: word);
  1478. {switches to |change_file| if the buffers match}
  1479. label done,exit;
  1480. var n:integer; {the number of discrepancies found}
  1481. @!k:0..buf_size; {index into the buffers}
  1482. SrcFile: word; {this file will be changed by another change file}
  1483. begin if lines_dont_match(newch) then return;
  1484. if ChgLevel=0 then
  1485.   SrcFile:= web_file
  1486. else
  1487.   SrcFile:= ChgFile[ChgAct[ChgLevel]];
  1488. n:=0;
  1489. incr(ChgLevel); {new change file}
  1490. ChgAct[ChgLevel]:= newch; {store index of new change file}
  1491. loop@+  begin
  1492.   incr(ChgLine[newch]);
  1493.   if not input_ln(ChgFile[newch]) then
  1494.     begin err_print('! Change file #',newch,' ended before @@y');
  1495. @.Change file ended...@>
  1496.     ChgLimit[newch]:=0;
  1497.     return;
  1498.     end;
  1499.   @<If the current line starts with \.{@@y},
  1500.     report any discrepancies and |return|@>;
  1501.   ChgLimit[newch]:= limit;
  1502.   move(buffer,ChgBuffer[newch],limit);
  1503.   if SrcFile=web_file then
  1504.     incr(line)
  1505.   else
  1506.     incr(ChgLine[ChgAct[ChgLevel-1]]);
  1507.  
  1508.   loop@+  begin
  1509.     if not input_ln(SrcFile) then begin
  1510.       if SrcFile=web_file then begin
  1511.         err_print('! WEB file ended during a change');
  1512. @.WEB file ended...@>
  1513.         input_has_ended:=true; return;
  1514.       end else
  1515.         @<Remove |ChgAct[ChgLevel-1]|@>
  1516.     end else begin
  1517.       if (SrcFile=web_file) or (limit<2) or (buffer[0]<>"@@")
  1518.       or ((buffer[1]<>"z") and (buffer[1]<>"Z")) then begin
  1519.         if SrcFile=web_file then
  1520.           incr(line)
  1521.         else
  1522.           incr(ChgLine[ChgAct[ChgLevel-1]]);
  1523.         goto done
  1524.       end else begin
  1525.         incr(ChgLine[ChgAct[ChgLevel-1]]);
  1526.         prime_the_change_buffer(ChgAct[ChgLevel-1]);
  1527.         @<Remove |ChgAct[ChgLevel-1]|@>
  1528.       end
  1529.     end
  1530.   end;
  1531.   done:
  1532.   if lines_dont_match(newch) then incr(n);
  1533.   end;
  1534. exit: end;
  1535.  
  1536. @ @<Remove |ChgAct[ChgLevel-1]|@>=
  1537. begin decr(ChgLevel);
  1538.   ChgAct[ChgLevel]:= ChgAct[ChgLevel+1];
  1539.   if ChgLevel=1 then
  1540.     SrcFile:= web_file
  1541.   else
  1542.     SrcFile:= ChgFile[ChgAct[ChgLevel-1]]
  1543. end;
  1544. @z
  1545. 2423
  1546. ────────────────────────────────────────────────────────────────
  1547. @x l.2439 m.134         - implement multiple change files
  1548. @ @<Initialize the input system@>=
  1549. open_input; line:=0; other_line:=0;@/
  1550. changing:=true; prime_the_change_buffer; change_changing;@/
  1551. limit:=0; loc:=1; buffer[0]:=" "; input_has_ended:=false;
  1552. @y
  1553. @ @<Initialize the input system@>=
  1554. open_input; line:=0;@/
  1555. ChgLevel:= ChgCnt;
  1556. while ChgLevel>0 do begin
  1557.   ChgLine[ChgLevel]:= 0;
  1558.   prime_the_change_buffer(ChgLevel);
  1559.   decr(ChgLevel)
  1560. end;
  1561. limit:=0; loc:=1; buffer[0]:=" "; input_has_ended:=false;
  1562. @z
  1563. ────────────────────────────────────────────────────────────────
  1564. @x l.2448 m.135         - implement multiple change files
  1565. @p procedure get_line; {inputs the next line}
  1566. label restart;
  1567. begin restart: if changing then
  1568.   @<Read from |change_file| and maybe turn off |changing|@>;
  1569. if not changing then
  1570.   begin @<Read from |web_file| and maybe turn on |changing|@>;
  1571.   if changing then goto restart;
  1572.   end;
  1573. loc:=0; buffer[limit]:=" ";
  1574. end;
  1575.  
  1576. @ @<Read from |web_file|...@>=
  1577. begin incr(line);
  1578. if not input_ln(web_file) then input_has_ended:=true
  1579. else if limit=change_limit then
  1580.   if buffer[0]=change_buffer[0] then
  1581.     if change_limit>0 then check_change;
  1582. end
  1583.  
  1584. @ @<Read from |change_file|...@>=
  1585. begin incr(line);
  1586. if not input_ln(change_file) then
  1587.   begin err_print('! Change file ended without @@z');
  1588. @.Change file ended...@>
  1589.   buffer[0]:="@@"; buffer[1]:="z"; limit:=2;
  1590.   end;
  1591. if limit>1 then {check if the change has ended}
  1592.   if buffer[0]="@@" then
  1593.     begin if (buffer[1]>="X")and(buffer[1]<="Z") then
  1594.       buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
  1595.     if (buffer[1]="x")or(buffer[1]="y") then
  1596.       begin loc:=2; err_print('! Where is the matching @@z?');
  1597. @.Where is the match...@>
  1598.       end
  1599.     else if buffer[1]="z" then
  1600.       begin prime_the_change_buffer; change_changing;
  1601.       end;
  1602.     end;
  1603. end
  1604.  
  1605. @ At the end of the program, we will tell the user if the change file
  1606. had a line that didn't match any relevant line in |web_file|.
  1607.  
  1608. @<Check that all changes have been read@>=
  1609. if change_limit<>0 then {|changing| is false}
  1610.   begin for loc:=0 to change_limit do buffer[loc]:=change_buffer[loc];
  1611.   limit:=change_limit; changing:=true; line:=other_line; loc:=change_limit;
  1612.   err_print('! Change file entry did not match');
  1613. @.Change file entry did not match@>
  1614.   end
  1615. @y
  1616. @p procedure get_line; {inputs the next line}
  1617. label restart,reswitch,continue;
  1618. var
  1619.   i,OldLevel: 0..ChangeMax;
  1620. begin restart: @/
  1621.   if ChgLevel>0 then
  1622.     @<Read the next line from a |ChgFile|@>;
  1623. if ChgLevel=0 then
  1624.   begin @<Read from |web_file| and maybe increment |ChgLevel|@>;
  1625.   if ChgLevel>0 then goto restart;
  1626.   end;
  1627. loc:=0; buffer[limit]:=" ";
  1628. end;
  1629.  
  1630. @ @<Read from |web_file|...@>=
  1631. begin incr(line);
  1632. if not input_ln(web_file) then input_has_ended:=true
  1633. else begin
  1634.   i:= 0;
  1635.   while (i<ChgCnt) and (ChgLevel=0) do begin
  1636.     incr(i);
  1637.     if (ChgLimit[i]=limit) and (buffer[0]=ChgBuffer[i][0])
  1638.     and (ChgLimit[i]>0) then
  1639.       check_change(i);
  1640.   end
  1641. end
  1642. end
  1643.  
  1644. @ @<Read the next...@>=
  1645. begin reswitch:
  1646.   incr(ChgLine[ChgAct[ChgLevel]]);
  1647.   if not input_ln(ChgFile[ChgAct[ChgLevel]]) then
  1648.     begin err_print('! Change file #',ChgAct[ChgLevel],' ended without @@z');
  1649. @.Change file ended...@>
  1650.       buffer[0]:="@@"; buffer[1]:="z"; limit:=2;
  1651.     end;
  1652. if (limit>1) and (buffer[0]="@@") then {check if the change has ended}
  1653.   case buffer[1] of
  1654.     "X","x","Y","y":
  1655.       begin loc:=2; err_print('! Where is the matching @@z?');
  1656. @.Where is the match...@>
  1657.         goto continue;
  1658.       end;
  1659.     "Z","z":
  1660.       begin
  1661.         prime_the_change_buffer(ChgAct[ChgLevel]);
  1662.         decr(ChgLevel);
  1663.         if ChgLevel>0 then goto reswitch; {read from previous CHG file}
  1664.         goto continue
  1665.       end;
  1666.  end; {case}
  1667.  i:= ChgAct[ChgLevel]; OldLevel:= ChgLevel;
  1668.  while (i<ChgCnt) and (OldLevel=ChgLevel) do begin
  1669.    incr(i);
  1670.    if (ChgLimit[i]=limit) and (buffer[0]=ChgBuffer[i][0])
  1671.    and (ChgLimit[i]>0) then
  1672.      check_change(i)
  1673.  end;
  1674.  continue:
  1675. end
  1676.  
  1677. @ At the end of the program, we will tell the user if the change file
  1678. had a line that didn't match any relevant line in |web_file|.
  1679.  
  1680. @<Check that all changes have been read@>=
  1681. for ChgLevel:= 1 to ChgCnt do if ChgLimit[ChgLevel]<>0 then
  1682.   begin
  1683.     move(ChgBuffer[ChgLevel],buffer,ChgLimit[ChgLevel]);
  1684.     limit:=ChgLimit[ChgLevel];
  1685.     loc:=ChgLimit[ChgLevel];
  1686.   err_print('! Change file #',ChgLevel,': entry did not match');
  1687. @.Change file entry did not match@>
  1688.   end
  1689. @z
  1690. ────────────────────────────────────────────────────────────────
  1691. @x l.2538 m.140         - implement an inline assembler
  1692. "\": control_code:=force_line; {force a new line in \PASCAL\ output}
  1693. @y
  1694. "\": control_code:=force_line; {force a new line in \PASCAL\ output}
  1695. "[": control_code:= asm_start;
  1696. "]": control_code:= asm_end;
  1697. @z
  1698. ────────────────────────────────────────────────────────────────
  1699. @x l.2653 m.146         - make use of Turbo's hex constants
  1700. "A",up_to("Z"),"a",up_to("z"): @<Get an identifier@>;
  1701. @y
  1702. "A".."Z","a".."z","_": @<Get an identifier@>;
  1703. "$": begin c:= hex; scanning_hex:= true end;
  1704. @z
  1705. ────────────────────────────────────────────────────────────────
  1706. @x l.2840 m.160
  1707. var accumulator:integer; {accumulates sums}
  1708. @y
  1709. var accumulator:LongInt; {accumulates sums}
  1710. @z
  1711. ────────────────────────────────────────────────────────────────
  1712. @x l.2843 m.160
  1713. @!val:integer; {constants being evaluated}
  1714. @y
  1715. @!val:LongInt; {constants being evaluated}
  1716. @z
  1717. ────────────────────────────────────────────────────────────────
  1718. @x l.2872 m.161
  1719.     add_in(equiv[q]-@'100000);
  1720. @y
  1721.     add_in(LongInt(equiv[q])-@'100000);
  1722. @z
  1723. ────────────────────────────────────────────────────────────────
  1724. @x l.2908 m.165         - make use of Turbo's hex constants
  1725. repeat if next_control>="A" then next_control:=next_control+"0"+10-"A";
  1726. @y
  1727. repeat
  1728. if next_control>="a" then next_control:=next_control+"0"+10-"a"
  1729. else if next_control>="A" then next_control:=next_control+"0"+10-"A";
  1730. @z
  1731. ────────────────────────────────────────────────────────────────
  1732. @x l.2952 m.168
  1733.   "#": if t=parametric then a:=param;
  1734. @y
  1735.   "#": if (t=parametric)or(t=array_type) then a:=param;
  1736. @z
  1737. ────────────────────────────────────────────────────────────────
  1738. @x l.3096 m.180
  1739. if next_control="(" then
  1740. @y
  1741. begin p:=parametric;
  1742. if next_control="[" then p:=array_type;
  1743. if (p=parametric)and(next_control="(")or
  1744.    (p=array_type)and(next_control="[") then
  1745. @z
  1746. ────────────────────────────────────────────────────────────────
  1747. @x l.3100 m.180
  1748.     if next_control=")" then
  1749. @y
  1750.     if (p=parametric)and(next_control=")")or
  1751.        (p=array_type)and(next_control="]") then
  1752. @z
  1753. ────────────────────────────────────────────────────────────────
  1754. @x l.3108 m.180
  1755.         begin define_macro(parametric); goto continue;
  1756. @y
  1757.         begin define_macro(p); goto continue;
  1758. @z
  1759. ────────────────────────────────────────────────────────────────
  1760. @x l.3112 m.180
  1761.   end;
  1762. @y
  1763.   end;
  1764. end
  1765. @z
  1766. ────────────────────────────────────────────────────────────────
  1767. @x l.3227 m.188
  1768. @p begin initialize;
  1769. @y
  1770. @p
  1771. var
  1772.   ExitSave: Pointer;
  1773. @={$F+}@>
  1774. procedure FinishUp;
  1775. @={$F-}@>
  1776. begin
  1777.   if ErrorAddr<>NIL then begin
  1778.     write_ln('Internal error #',ExitCode);
  1779.     ErrorAddr:= NIL;
  1780.     Halt(ExitCode)
  1781.   end;
  1782. if string_ptr>256 then begin
  1783.   @<Finish off the string pool file@>;
  1784.   Close(pool)
  1785. end else
  1786.   Erase(pool);
  1787. stat @<Print statistics about memory usage@>;@+tats@;@/
  1788. @<Print the job |history|@>;
  1789. @<Close all Files@>;
  1790. mfree(byte_seg);
  1791. mfree(tok_seg);
  1792. mfree(Pascal_buffer);
  1793. ExitProc:= ExitSave;
  1794. end; { FinishUp }
  1795.  
  1796. begin { main }
  1797.   ExitSave:= ExitProc;
  1798.   ExitProc:= @@FinishUp;
  1799.  
  1800.   fillchar(firstvar,Ofs(lastvar)-Ofs(firstvar),0);
  1801.   if (ParamCount=0) then begin
  1802.     print_ln(banner);
  1803.     print_ln('Usage: TANGLE <WEB file> [<CHG file1>] [<CHG file2>...]'
  1804.                   +' [Options]');
  1805.     print_ln('Options: /d optimize DIV');
  1806.     print_ln('         /m optimize MOD');
  1807.     print_ln('         /c include comments in inlines');
  1808.     print_ln('');
  1809.     Halt(error_message)
  1810.   end;
  1811.   initialize;
  1812. @z
  1813. ────────────────────────────────────────────────────────────────
  1814. @x l.3233 m.188
  1815. end_of_TANGLE:
  1816. if string_ptr>256 then @<Finish off the string pool file@>;
  1817. stat @<Print statistics about memory usage@>;@+tats@;@/
  1818. @t\4\4@>{here files should be closed if the operating system requires it}
  1819. @<Print the job |history|@>;
  1820. @y
  1821. @z
  1822. -- 3286
  1823. ────────────────────────────────────────────────────────────────
  1824. @x l.3293 m.194
  1825. itself will get a new module number.
  1826. @^system dependencies@>
  1827. @y
  1828. itself will get a new module number.
  1829. @^system dependencies@>
  1830.  
  1831. Here we add the more extensive changes for this \.{TP} version
  1832. of \.{TANGLE}.
  1833.  
  1834. @ First we need an extra module to copy the parameter of an |array_type|
  1835. macro.
  1836.  
  1837. @<Copy the array parameter...@>=
  1838. bal:= 1; incr(cur_byte); {skip the opening '[' }
  1839. repeat b:=tok_mem[zo,cur_byte]; incr(cur_byte);
  1840.   if b=param then store_two_bytes(word(name_ptr)+@'77777)
  1841.   else  begin if b>=@'200 then
  1842.       begin app_repl(b);
  1843.       b:=tok_mem[zo,cur_byte]; incr(cur_byte);
  1844.       end
  1845.     else case b of
  1846.       "[": incr(bal);
  1847.       "]": decr(bal);
  1848. {      ",": if bal=1 then begin
  1849.              decr(cur_byte);
  1850.              tok_mem[zo,cur_byte]:="[";
  1851.              bal:= 0
  1852.            end;
  1853. }
  1854.       "'": repeat app_repl(b);
  1855.         b:=tok_mem[zo,cur_byte]; incr(cur_byte);
  1856.         until b="'"; {copy string, don't change |bal|}
  1857.       othercases do_nothing
  1858.       endcases;
  1859.     if bal<>0 then app_repl(b)
  1860.   end
  1861. until bal=0
  1862.  
  1863. @ The following function returns a commandline parameter without
  1864.   an option
  1865.  
  1866. @<All purpose procedures and functions@>=
  1867.    function Parameter (i: word): String;
  1868.    var
  1869.      p: word;
  1870.      s: String;
  1871.    begin
  1872.      s:= ParamStr(i);
  1873.      p:= pos('-',s);
  1874.      if p=0 then p:= pos('/',s);
  1875.      if p>0 then byte(s[0]):= p-1;
  1876.      Parameter:= s
  1877.    end;
  1878. @z
  1879.